﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.ComponentModel.Composition.Primitives;
using System.Reflection;
namespace MEFedMVVM.ViewModelLocator
{
    /// <summary>
    /// Default composer for Design time. This will load all assemblies that have the DesignTimeCatalog attibute
    /// </summary>
    public class DefaultDesignTimeComposer : IComposer
    {
        private const string MefedmvvmWpf = "MEFedMVVM.WPF";
        private const string MefedmvvmWpfDll = "MEFedMVVM.WPF.dll";

        #region IComposer Members

        AggregateCatalog m_catalog;
        public AggregateCatalog CataLog
        {
            get
            {
                if (m_catalog == null)
                {
                    m_catalog = GetCatalog();
                }
                else
                {
                    AppendCatalog();
                }
                return m_catalog;
            }
            set
            {
                m_catalog = value;
            }
        }
        public ComposablePartCatalog GetContainer()
        {
            return CataLog;
        }
        /// <summary>
        /// Use this to return the list of Custom Export providers you want to be used in the main composition
        /// return null if you do not want to retun any custom export providers
        /// </summary>
        /// <returns>Return a list of Export Providers to be used</returns>
        public IEnumerable<ExportProvider> GetCustomExportProviders()
        {
            return null;
        }

        #endregion

        private AggregateCatalog GetCatalog()
        {
            //CAS - Modified to work around Blend’s caching of different versions of an assembly at the same time.
            //This will ensure that only the newest assembly found is actually in the catalog.

            Dictionary<string, AssemblyCatalog> assemDict = new Dictionary<string, AssemblyCatalog>();


            IList<AssemblyCatalog> assembliesLoadedCatalogs =
                (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                 //only load assemblyies with this attribute
                 where assembly.GetReferencedAssemblies().Where(x => x.Name.Contains(MefedmvvmWpf)).Count() > 0 ||
                 assembly.ManifestModule.Name == MefedmvvmWpfDll &&
                 !ShouldIgnoreAtDesignTime(assembly)
                 select new AssemblyCatalog(assembly)).ToList();

            if (assembliesLoadedCatalogs.Where(x => x.Assembly.ManifestModule.Name != MefedmvvmWpfDll).Count() == 0)
            {
                Debug.WriteLine("No assemblies found for Design time. Quick tip... ");
                return null;
            }

            var catalog = new AggregateCatalog();

            foreach (var item in assembliesLoadedCatalogs)
            {
                AssemblyCatalog ass;
                if (assemDict.TryGetValue(item.Assembly.FullName, out ass))
                {
                    DateTime oldAssDT = File.GetLastAccessTime(ass.Assembly.Location);
                    DateTime newAssDT = File.GetLastAccessTime(item.Assembly.Location);
                    if (newAssDT > oldAssDT)
                    {
                        assemDict[item.Assembly.FullName] = item;
                    }
                }
                else
                {
                    assemDict[item.Assembly.FullName] = item;
                }
            }

            foreach (var item in assemDict.Values)
                catalog.Catalogs.Add(item);
            return catalog;
        }
        private AggregateCatalog AppendCatalog()
        {
            var catalog = new AggregateCatalog();


            return catalog;

        }
        private static bool ShouldIgnoreAtDesignTime(Assembly assembly)
        {
            object[] customAttributes = assembly.GetCustomAttributes(typeof(IgnoreAtDesignTimeAttribute), true);
            return customAttributes != null && customAttributes.Length != 0;
        }
    }

    /// <summary>
    /// Implementation for a default runtime composer
    /// </summary>
    public class DefaultRuntimeComposer : IComposer
    {
        private const string MefedmvvmWpf = "MEFedMVVM.WPF";
        private const string MefedmvvmWpfDll = "MEFedMVVM.WPF.dll";
        #region IComposer Members
        AggregateCatalog m_catalog;
        ArrayList LoadedAssembly;
        public ComposablePartCatalog GetContainer()
        {
            return CataLog;
        }
        public AggregateCatalog CataLog
        {
            get
            {
                if (m_catalog == null)
                {
                    m_catalog = GetCatalog();
                }
                else
                {
                    AppendCatalog();
                }
                return m_catalog;
            }
            set
            {
                m_catalog = value;
            }
        }
        /// <summary>
        /// Use this to return the list of Custom Export providers you want to be used in the main composition
        /// return null if you do not want to retun any custom export providers
        /// </summary>
        /// <returns>Return a list of Export Providers to be used</returns>
        public IEnumerable<ExportProvider> GetCustomExportProviders()
        {
            return null;
        }

        #endregion
        private void AppendCatalog()
        {
            //CAS - Modified to work around Blend’s caching of different versions of an assembly at the same time.
            //This will ensure that only the newest assembly found is actually in the catalog.
            Dictionary<string, AssemblyCatalog> assemDict = new Dictionary<string, AssemblyCatalog>();
            IList<AssemblyCatalog> assembliesLoadedCatalogs =
                        (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                         //only load assemblyies with this attribute
                         where
                         assembly.GetReferencedAssemblies().Where(x => x.Name.Contains(MefedmvvmWpf)).Count() > 0 ||
                         assembly.ManifestModule.Name == MefedmvvmWpfDll
                         select new AssemblyCatalog(assembly)).ToList();

            foreach (AssemblyCatalog item in assembliesLoadedCatalogs)
            {
                string file = item.Assembly.Location.ToUpper();
                if (LoadedAssembly.IndexOf(file) < 0)
                {
                    LoadedAssembly.Add(file);
                    m_catalog.Catalogs.Add(item);
                }
            }
        }
        private AggregateCatalog GetCatalog()
        {
            var catalog = new AggregateCatalog();
            DirectoryCatalog dc;
            var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            dc = new DirectoryCatalog(baseDirectory);
            catalog.Catalogs.Add(dc);
            dc = new DirectoryCatalog(baseDirectory, "*.exe");
            catalog.Catalogs.Add(dc);
            /*
            dc = new DirectoryCatalog(baseDirectory + "CLSLib\\");
            catalog.Catalogs.Add(dc);
            dc = new DirectoryCatalog(baseDirectory + "DBLib\\");
            catalog.Catalogs.Add(dc);
            dc = new DirectoryCatalog(baseDirectory + "WFLib\\");
            catalog.Catalogs.Add(dc);
            dc = new DirectoryCatalog(baseDirectory + "UILib\\");
            catalog.Catalogs.Add(dc);
            dc = new DirectoryCatalog(baseDirectory + "SharedDll\\");
            catalog.Catalogs.Add(dc);*/
            if (LoadedAssembly == null)
                LoadedAssembly = new ArrayList();
            foreach (var cat in catalog.Catalogs)
            {
                if (cat is DirectoryCatalog)
                {
                    foreach (string file in ((DirectoryCatalog)cat).LoadedFiles)
                    {
                        if (LoadedAssembly.IndexOf(file.ToUpper()) < 0)
                        {
                            LoadedAssembly.Add(file.ToUpper());
                        }
                    }
                }
            }
            return catalog;
        }
    }
}
